﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using XDL.Framework.Weapons;
using XDL.Framework.Particles;
using XDL.Framework;
using JumpNFight.Simulation;

namespace JumpNFight.Weapons
{
    abstract class BulletWeaponSystemBase :
        GameComponent,
        IWeapon
    {
        AudioManager m_audio;
        ParticleSystem m_bulletPS;
        ParticleSystem m_impactPS;
        ParticleSystem m_shellPS;
        World m_world;
        protected readonly static Random Rnd = new Random();

        protected World World
        {
            get
            {
                return m_world;
            }
        }

        protected AudioManager Audio
        {
            get
            {
                return m_audio;
            }
        }

        public BulletWeaponSystemBase(Game game) :
            base(game)
        {
        }

        protected abstract string BulletPSName
        {
            get;
        }

        protected abstract string WeaponName
        {
            get;
        }

        protected virtual int ShellCount
        {
            get
            {
                return 0;
            }
        }

        /// <summary>
        /// Returns a trail particle system
        /// </summary>
        /// <remarks>
        /// Return null for no trail
        /// </remarks>
        protected virtual int TrailCount
        {
            get
            {
                return 0;
            }
        }

        protected virtual ParticleSystem TrailParticleSystem
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Returns the number of bullet to fire simultaneously
        /// </summary>
        protected virtual int BulletCount
        {
            get
            {
                return 1;
            }
        }

        /// <summary>
        /// Indicates whether the weapon should shake the world
        /// </summary>
        protected virtual bool ShakeWorldOnHit
        {
            get
            {
                return false;
            }
        }

        protected virtual bool ShakeWorldOnFire
        {
            get
            {
                return false;
            }
        }

        public override void Initialize()
        {
            Settings = Game.Content.Load<WeaponSettings>(WeaponName);
            m_bulletPS = GameServices.GetService<ParticleSystem>(BulletPSName);
            m_audio = GameServices.GetService<AudioManager>();
            m_impactPS = GameServices.GetService<ParticleSystem>("Particles/BulletImpact");
            m_bulletPS.OnParticleUpdate = ParticleUpdateHandler;
            if ( ShellCount > 0 )
                m_shellPS = GameServices.GetService<ParticleSystem>("Particles/" + Settings.Name + "Shell");
            m_world = GameServices.GetService<World>();
        }

        public WeaponSettings Settings
        {
            get;
            private set;
        }

        public void Fire(int ownerID, Vector3 position, Vector3 direction)
        {
            for (int i = 0; i < BulletCount; i++)
                m_bulletPS.AddParticle(ownerID, position, direction);

            if (ShakeWorldOnFire)
                m_world.Shake();

            // per-weapon sound
            m_audio.PlayCue(Settings.Name + "/Fire");
            
            for (int i = 0; i < ShellCount; i++)
                m_shellPS.AddParticle(new Vector3(position.X - direction.X * 16, position.Y, 0), new Vector3(0, -1, 0));
        }

        static Random m_rnd = new Random();
        bool ParticleUpdateHandler(GameTime gameTime, ParticleVertex oldParticle, ref ParticleVertex particle)
        {
            Vector3 direction = particle.Position - oldParticle.Position;
            Vector3 normalizedDirection = Vector3.Normalize(direction);
            
            for(int i=0;i<TrailCount;i++)
                TrailParticleSystem.AddParticle(Vector3.Lerp(oldParticle.Position, particle.Position,i / (float)TrailCount),Vector3.Zero);

            float distance = direction.Length();
            Nullable<Intersection> intersection = m_world.Intersect(
                oldParticle.Position, 
                normalizedDirection,
                distance,
                Settings.Radius);
            if (intersection.HasValue)
            {
                return Hit(intersection.Value, (int)oldParticle.Tag, ref particle, oldParticle.Position, normalizedDirection, distance);
            }
            
            return true;
        }

        protected virtual bool Hit(Intersection intersection, int tag, ref ParticleVertex particle, Vector3 position, Vector3 normalizedDirection, float distance)
        {
            if (intersection.IsTile)
            {
                // spawn impact
                for (int i = 0; i < Rnd.Next(1, 3); i++)
                    m_impactPS.AddParticle(position, new Vector3(-100 * normalizedDirection.X, 0, 0));
            }
            else
            {
                // we hit "someone"!!
                int playerID = tag;
                GameServices.EventDispatcher.PostEvent(intersection.TargetID, NPCBase.HitEvent, new WeaponHitEventArgs(playerID, this.Settings));
                GameServices.EventDispatcher.PostEvent(playerID, Player.RegisterHitEvent, null);
            }

            if (ShakeWorldOnHit)
                m_world.Shake();

            // kill the particle
            return false;
        }
    }
}
